// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
// SPDX-License-Identifier: MIT
package utils

import (
    "os"
    "sync"
    "syscall"
    "testing"
    "time"

    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/require"
)

func setup() {
    StopSignalHandlers()
}

func TestHandleSignals_Basic(t *testing.T) {
    setup()
    defer StopSignalHandlers()

    var callbackCalled bool
    var callbackMutex sync.Mutex

    callback := func() {
        callbackMutex.Lock()
        defer callbackMutex.Unlock()
        callbackCalled = true
    }

    HandleSignals(callback)

    time.Sleep(100 * time.Millisecond)

    process, err := os.FindProcess(os.Getpid())
    require.NoError(t, err)

    err = process.Signal(syscall.SIGTERM)
    require.NoError(t, err)

    time.Sleep(100 * time.Millisecond)

    callbackMutex.Lock()
    defer callbackMutex.Unlock()
    assert.True(t, callbackCalled, "Callback should have been called")
}

func TestHandleSignals_SIGINT(t *testing.T) {
    setup()
    defer StopSignalHandlers()

    var callbackCalled bool
    var callbackMutex sync.Mutex

    callback := func() {
        callbackMutex.Lock()
        defer callbackMutex.Unlock()
        callbackCalled = true
    }

    HandleSignals(callback)
    time.Sleep(50 * time.Millisecond)

    process, err := os.FindProcess(os.Getpid())
    require.NoError(t, err)

    err = process.Signal(syscall.SIGINT)
    require.NoError(t, err)

    time.Sleep(100 * time.Millisecond)

    callbackMutex.Lock()
    defer callbackMutex.Unlock()
    assert.True(t, callbackCalled, "Callback should have been called for SIGINT")
}

func TestHandleSignals_ReplaceHandler(t *testing.T) {
    setup()
    defer StopSignalHandlers()

    callCount := 0
    var mutex sync.Mutex

    callback1 := func() {
        mutex.Lock()
        defer mutex.Unlock()
        callCount++
    }

    callback2 := func() {
        mutex.Lock()
        defer mutex.Unlock()
        callCount += 10
    }

    HandleSignals(callback1)
    time.Sleep(50 * time.Millisecond)

    HandleSignals(callback2)
    time.Sleep(50 * time.Millisecond)

    process, err := os.FindProcess(os.Getpid())
    require.NoError(t, err)

    err = process.Signal(syscall.SIGTERM)
    require.NoError(t, err)

    time.Sleep(100 * time.Millisecond)

    mutex.Lock()
    defer mutex.Unlock()
    assert.Equal(t, 10, callCount, "Only the second callback should be called")
}

func TestHandleSignals_MultipleSignals(t *testing.T) {
    var callCount int
    var mutex sync.Mutex

    callback := func() {
        mutex.Lock()
        defer mutex.Unlock()
        callCount++
    }

    go HandleSignals(callback)
    time.Sleep(50 * time.Millisecond)

    process, err := os.FindProcess(os.Getpid())
    require.NoError(t, err)

    for i := 0; i < 3; i++ {
        err = process.Signal(syscall.SIGTERM)
        require.NoError(t, err)
        time.Sleep(50 * time.Millisecond)
    }

    time.Sleep(100 * time.Millisecond)

    mutex.Lock()
    defer mutex.Unlock()
    assert.Equal(t, 1, callCount, "Callback should be called exactly once")
}

func TestHandleSignals_ConcurrentSafety(t *testing.T) {
    var callbackCalled bool
    var callbackMutex sync.Mutex
    var wg sync.WaitGroup

    wg.Add(1)

    callback := func() {
        callbackMutex.Lock()
        defer callbackMutex.Unlock()
        if !callbackCalled {
            callbackCalled = true
            wg.Done()
        }
    }

    go HandleSignals(callback)

    time.Sleep(100 * time.Millisecond)

    process, err := os.FindProcess(os.Getpid())
    require.NoError(t, err)

    err = process.Signal(syscall.SIGTERM)
    require.NoError(t, err)

    done := make(chan bool)
    go func() {
        wg.Wait()
        done <- true
    }()

    select {
    case <-done:
    case <-time.After(time.Second):
        t.Fatal("测试超时")
    }

    callbackMutex.Lock()
    defer callbackMutex.Unlock()
    assert.True(t, callbackCalled, "Callback should have been called")
}
